#! python
# -*- coding: utf-8 -*-

__author__ = 'Anton Vakhrushev'


import wx
import datetime
import wx.lib.agw.aui as wxaui
from lib.widgets import imglistctrl
from lib.controllers import progressctr
from lib.extra import utils


class PhotoImage(imglistctrl.BaseImage):

    ICONS = {}

    def __init__(self, owner, photo):
        """
        @type photo: photo.MatchedPhoto
        """
        imglistctrl.BaseImage.__init__(self, owner, photo)
        if not self.ICONS:
            self.ICONS = {
                'PHOTO_YANDEX': wx.Bitmap('share/picture.png'),
                'PHOTO_LOCAL': wx.Bitmap('share/picture_add.png'),
                'PHOTO_MATCHED': wx.Bitmap('share/picture_link.png'),
                'PHOTO_EDITED': wx.Bitmap('share/picture_edit.png'),
                'PHOTO_ERROR': wx.Bitmap('share/picture_error.png'),
            }

    def LoadImage(self, w, h):
        return self.data.wximage(w, h)

    def DrawBadges(self, dc, width, height, index, selected):
        badges = []
        if self.data.is_edited():
            badges.append(self.ICONS['PHOTO_EDITED'])
        elif self.data.is_matched():
            badges.append(self.ICONS['PHOTO_MATCHED'])
        elif self.data.is_local():
            size = self.data.size / 1024.0 / 1024.0 if self.data.size else None
            if size > 20:
                badges.append(self.ICONS['PHOTO_ERROR'])
            else:
                badges.append(self.ICONS['PHOTO_LOCAL'])
        else:
            badges.append(self.ICONS['PHOTO_YANDEX'])

        fx, fy, fw, fh = self.GetFrameRect(width, height)
        for index, badge in enumerate(badges):
            margin = 5 + 21 * index
            dc.DrawBitmap(badge, fx + margin, fy + fh - 21)


class IconStorage:
    def __init__(self):
        self.imageList = wx.ImageList(16, 16)
        self.YANDEX_FOTKI = self.imageList.Add(wx.Bitmap('share/yandex_fotki.png'))
        self.PHOTOS = self.imageList.Add(wx.Bitmap('share/photos.png'))
        self.ALBUM_YANDEX = self.imageList.Add(wx.Bitmap('share/folder_picture.png'))
        self.ALBUM_LOCAL = self.imageList.Add(wx.Bitmap('share/folder_add.png'))
        self.ALBUM_MATCHED = self.imageList.Add(wx.Bitmap('share/folder_link.png'))
        self.ALBUM_SYNCED = self.imageList.Add(wx.Bitmap('share/folder_search.png'))
        self.ALBUM_EDITED = self.imageList.Add(wx.Bitmap('share/folder_green.png'))
        self.PHOTO_YANDEX = self.imageList.Add(wx.Bitmap('share/photo.png'))
        self.PHOTO_LOCAL = self.imageList.Add(wx.Bitmap('share/photo_add.png'))
        self.PHOTO_MATCHED = self.imageList.Add(wx.Bitmap('share/photo_link.png'))

    def GetIcon(self, index):
        return self.imageList.GetIcon(index)


class IdStorage:
    def __init__(self):
        self.ID_LOG_IN = wx.NewId()
        self.ID_MENU_FILE_OPEN = wx.NewId()
        self.ID_MENU_UPLOAD = wx.NewId()
        self.ID_OPEN_SELECTED_PHOTO = wx.NewId()
        self.ID_DELETE_SELECTED_PHOTO = wx.NewId()
        self.ID_ALBUMS_REFRESH = wx.NewId()
        self.ID_ALBUM_CREATE = wx.NewId()
        self.ID_ALBUM_RENAME = wx.NewId()
        self.ID_ALBUM_SETTINGS = wx.NewId()
        self.ID_CURRENT_ALBUM_DELETE = wx.NewId()
        self.ID_CURRENT_ALBUM_UPLOAD = wx.NewId()
        self.ID_PHOTO_UPLOAD = wx.NewId()


class FindSuccessful(Exception):
    def __init__(self, item_id):
        self.item_id = item_id


class OverviewForm(wx.Frame):
    def __init__(self, controller, title):
        """
        Главная форма
        """
        wx.Frame.__init__(self, None, wx.NewId(), title, size=(1100, 750))

        self.title = title
        self.controller = controller
        """@type : OverviewController"""
        self.icons = IconStorage()
        self.ids = IdStorage()
        self.current_album_id = None
        self.current_album_item_id = None
        self.selected_album_id = None

        menubar = wx.MenuBar()
        menu = wx.Menu()
        menu.Append(self.ids.ID_LOG_IN, u"&Войти\tCtrl+L")
        menubar.Append(menu, u'&Сервис')
        menu = wx.Menu()
        menu.Append(self.ids.ID_ALBUMS_REFRESH, u"Обновить все\tCtrl+R")
        menu.Append(self.ids.ID_CURRENT_ALBUM_UPLOAD, u"Загрузить\tCtrl+U")
        menu.Append(self.ids.ID_CURRENT_ALBUM_DELETE, u"Удалить\tCtrl+D")
        menubar.Append(menu, u'Альбом')
        menu = wx.Menu()
        menu.Append(self.ids.ID_OPEN_SELECTED_PHOTO, u"&Просмотр\tF12")
        menubar.Append(menu, u'&Фотография')
        self.SetMenuBar(menubar)

        # aui manager
        self.auimgr = wxaui.AuiManager()
        self.auimgr.SetManagedWindow(self)
        self.auimgr.GetArtProvider().SetMetric(wxaui.AUI_DOCKART_SASH_SIZE, 3)
        # album tree
        self.albumTree = wx.TreeCtrl(self, size=(300, -1))
        self.albumTree.SetImageList(self.icons.imageList)
        self.auimgr.AddPane(self.albumTree,
                            wxaui.AuiPaneInfo().Name('albumTree').Left().Layer(1)
                            .CloseButton(False).CaptionVisible(False))
        # image list
        self.thumbs = imglistctrl.ImageListCtrl(self)
        self.thumbs.SetBackgroundColour('#505050')
        self.thumbs.SetSelectionColour('#e5da29')
        self.thumbs.SetThumbnailSize(180, 180)
        self.auimgr.AddPane(self.thumbs,
                            wxaui.AuiPaneInfo().Name("thumbs").CenterPane().Position(1))
        self.auimgr.Update()

        self.SetIcon(self.icons.GetIcon(self.icons.YANDEX_FOTKI))
        self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize)

        sbar = wx.StatusBar(self)
        fields = [150, 150, 600] # login, photos, actions
        sbar.SetFieldsCount(len(fields))
        sbar.SetStatusWidths(fields)
        self.SetStatusBar(sbar)

        self.Bind(wx.EVT_CLOSE, self.OnClose)
        self.Bind(wx.EVT_MENU, self.OnLogin, id=self.ids.ID_LOG_IN)
        self.Bind(wx.EVT_MENU, self.OnAlbumsRefresh, id=self.ids.ID_ALBUMS_REFRESH)
        self.Bind(wx.EVT_MENU, self.OnAlbumCreate, id=self.ids.ID_ALBUM_CREATE)
        self.Bind(wx.EVT_MENU, self.OnAlbumSettings, id=self.ids.ID_ALBUM_SETTINGS)
        self.Bind(wx.EVT_MENU, self.OnDeleteCurrentAlbum, id=self.ids.ID_CURRENT_ALBUM_DELETE)
        self.Bind(wx.EVT_MENU, self.OnUploadCurrentAlbum, id=self.ids.ID_CURRENT_ALBUM_UPLOAD)
        self.Bind(wx.EVT_MENU, self.OnOpenPhoto, id=self.ids.ID_OPEN_SELECTED_PHOTO)

        self.thumbs.Bind(wx.EVT_KEY_DOWN, self.OnChar)
        self.thumbs.Bind(wx.EVT_LEFT_DCLICK, self.OnOpenPhoto)
        self.thumbs.Bind(imglistctrl.EVT_ILC_IMAGE_SELECTED, self.OnImageSelected)
        self.albumTree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnAlbumTreeSelectionChanged)
        self.albumTree.Bind(wx.EVT_TREE_ITEM_MENU, self.OnAlbumTreeContextMenu)
        self.albumTree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnAlbumSelected)

    # События формы ------------------------------------------------------------

    def OnClose(self, event):
        self.controller.exit()
        self.Destroy()

    def OnImageSelected(self, evt):
        img = evt.GetImage()
        selected = self.thumbs.GetSelected()
        if len(selected) == 1:
            ph = selected[0].data
            size = round(ph.size / 1024.0 / 1024.0, 2) if ph.size else '??'
            self.SetStatusText(u'Фотография "{}", размер {} MB'.format(ph.title, size), 2)
        if len(selected) == 2:
            diff = selected[0].data.similar_to(selected[1].data)
            self.SetStatusText(u'Разность: {}'.format(diff), 2)

    def OnChar(self, evt):
        keycode = evt.GetKeyCode()
        if keycode == wx.WXK_RETURN:
            self.OpenSelectedPhoto()
        if keycode == ord('X'):
            self.controller.copy_photo_links(self.thumbs.GetSelectedData())
        if keycode == ord('C'):
            keycode = ord('1')
        keycode -= ord('0')
        if evt.GetModifiers() == wx.MOD_CONTROL and keycode in range(1, 10):
            self.controller.copy_photos(self.thumbs.GetSelectedData(), keycode)

    def OnAlbumTreeSelectionChanged(self, event):
        item_id = event.GetItem()
        album = self.albumTree.GetPyData(item_id)
        self.selected_album_id = album.id
        print album.id

    def OnAlbumTreeContextMenu(self, event):
        item_id = event.GetItem()
        self.albumTree.SelectItem(item_id)
        albumMenu = wx.Menu()
        # albumMenu.Append(self.ids.ID_ALBUM_CREATE, u'Новый')
        # albumMenu.Append(self.ids.ID_ALBUM_DELETE, u'Удалить')
        # albumMenu.Append(self.ids.ID_ALBUM_UPLOAD, u'Загрузить')
        albumMenu.Append(self.ids.ID_ALBUM_SETTINGS, u'Настройки')
        self.albumTree.PopupMenu(albumMenu)
        albumMenu.Destroy()

    def OnAlbumSelected(self, event):
        """При выборе альбома"""
        item_id = event.GetItem()
        album = self.albumTree.GetPyData(item_id)
        self.controller.load_album(album)
        self.current_album_id = album.id
        self.current_album_item_id = item_id

    def OnLogin(self, event):
        self.controller.login()

    def OnUpload(self, event):
        pass

    def OnOpenPhoto(self, event):
        photos = self.thumbs.GetItemsData()
        index = self.thumbs.GetSelectionIndex()
        self.controller.open_photo(photos, index)

    def OnDeletePhoto(self, event):
        f = lambda p: p.data.delete()
        self.ForEachSelected(f, u'Удалить фотографии?')

    def OnSmartSyncSettings(self, event):
        self.OpenSmartSyncSettings()

    def OnAlbumsRefresh(self, event):
        self.controller.refresh_albums()

    def OnAlbumCreate(self, event):
        self.CreateAlbumInSelected()

    def OnDeleteCurrentAlbum(self, event):
        album = self.albumTree.GetPyData(self.current_album_item_id)
        self.controller.delete_album(album)

    def OnUploadCurrentAlbum(self, event):
        album = self.albumTree.GetPyData(self.current_album_item_id)
        self.controller.upload_album(album)

    def OnAlbumSettings(self, event):
        item_id = self.albumTree.GetSelection()
        album = self.albumTree.GetPyData(item_id)
        self.controller.album_settings(album)

    def OnUploadPhoto(self, event):
        self.UploadSelectedPhoto()

    # Функции старта и окончания работы приложения -----------------------------

    def ClearAll(self):
        self.SetTitle(self.title)
        self.albumTree.DeleteAllItems()
        self.thumbs.Clear()

    def SetCurrentAlbum(self, album):
        self.current_album_id = album.id
        self.current_album_item_id = self.FindAlbumItemId(album.id)

    def GetCurrentAlbum(self):
        album = self.albumTree.GetPyData(self.current_album_item_id)
        return self.current_album_item_id, album

    def ShowAlbumTree(self, root_album):
        """
        Выводит предварительно созданное дерево альбомов
        """
        def process(item_id, album):
            children = sorted(album.children, key=lambda a: a.title)
            for child in children:
                child_id = self.AppendAlbum(item_id, child)
                process(child_id, child)

        if not root_album:
            return
        self.albumTree.DeleteAllItems()
        root_id = self.albumTree.AddRoot(root_album.title)
        self.albumTree.SetPyData(root_id, root_album)
        self.albumTree.SetItemImage(root_id, self.icons.PHOTOS)
        process(root_id, root_album)
        self.albumTree.Expand(root_id)

    def ShowPhotos(self, photos, progress=None):

        def process_photo(photo):
            photo_image = PhotoImage(self.thumbs, photo)
            photo_image.Load()
            return photo_image

        key = lambda p: p.get_capture_time() or datetime.datetime(2000, 1, 1)
        photos = sorted(photos, key=key)
        progress = progressctr.Progress.cover(progress)
        photo_image_list = list(progress.map(process_photo, photos, title=u'Создаем миниатюры',
                                             message=lambda i, x: x.title))
        self.thumbs.AssignImages(photo_image_list)
        self.thumbs.Refresh()

    def GetSelectedAlbum(self):
        item_id = self.albumTree.GetSelection()
        return item_id, self.albumTree.GetPyData(item_id)

    def GetSelectedPhotos(self):
        return self.thumbs.GetSelected()

    def AppendAlbum(self, root_id, album):
        # задаем название
        item_id = self.albumTree.AppendItem(root_id, u'')
        self.albumTree.SetPyData(item_id, album)
        self.UpdateAlbumItem(item_id, album)
        return item_id

    def FindAlbumItemId(self, album_id):
        def WalkAlbumTree(node_id):
            data = self.albumTree.GetPyData(node_id)
            if data.id == album_id:
                raise FindSuccessful(node_id)
            child_id, cookie = self.albumTree.GetFirstChild(node_id)
            while child_id.IsOk():
                WalkAlbumTree(child_id)
                child_id, cookie = self.albumTree.GetNextChild(node_id, cookie)
        try:
            WalkAlbumTree(self.albumTree.GetRootItem())
            return None
        except FindSuccessful as exc:
            return exc.item_id

    def UpdateAlbumItem(self, item_id, album):
        """
        @type album: PhotoAlbum
        """
        title = unicode(album.title)
        if album.is_synced():
            self.albumTree.SetItemImage(item_id, self.icons.ALBUM_SYNCED)
        elif album.is_matched():
            if not album.children:
                title += u' ({}/{})'.format(album.count_remote(), album.count_local())
            if album.is_edited():
                self.albumTree.SetItemImage(item_id, self.icons.ALBUM_EDITED)
            else:
                self.albumTree.SetItemImage(item_id, self.icons.ALBUM_MATCHED)
        elif album.is_local():
            title += u' ({})'.format(album.count_local())
            self.albumTree.SetItemImage(item_id, self.icons.ALBUM_LOCAL)
        else:
            if not album.children:
                title += u' ({})'.format(album.count_remote())
            self.albumTree.SetItemImage(item_id, self.icons.ALBUM_YANDEX)
        self.albumTree.SetItemText(item_id, title)

    def UpdateAlbumView(self, album):
        item_id = self.FindAlbumItemId(album.id)
        if item_id:
            self.UpdateAlbumItem(item_id, album)

    def ReselectAlbum(self):
        if not self.selected_album_id:
            return
        item_id = self.FindAlbumItemId(self.selected_album_id)
        if item_id:
            self.albumTree.SelectItem(item_id)
            return
        item_id = self.FindAlbumItemId(self.current_album_id)
        if item_id:
            self.albumTree.SelectItem(item_id)

    # -------------------------------------------------------------------------

    def ForEachSelected(self, callback, query=None):
        selected = self.thumbs.GetSelected()
        agreed = not query or wx.MessageBox(query, u'Внимание', wx.OK | wx.CANCEL) == wx.OK
        if not (selected and agreed):
            return

        @utils.http_handler
        @utils.long_time
        def processor():
            count = len(selected) + 1
            pdlg = wx.ProgressDialog(title=u'Выполняем операцию...',
                                     message=u'Выполняем операцию...',
                                     maximum=count,
                                     parent=self)
            try:
                for idx, photo in enumerate(selected):
                    pdlg.Update(idx, u'Фотография {}: {}'.format(idx + 1, photo.data.title))
                    callback(photo)
                pdlg.Update(count, u'Обновление альбома')
                self.LoadAlbum(self.current_album)
            finally:
                pdlg.Destroy()

        processor()

    def OpenSmartSyncSettings(self):
        item_id = self.albumTree.GetSelection()
        album = self.albumTree.GetPyData(item_id)
        print album.id

    def CreateAlbumInSelected(self):
        item_id, album = self.GetSelectedAlbum()
        if album.count:
            wx.MessageBox(u'Нельзя создать альбом в непустом альбоме', u'Ошибка', wx.OK | wx.ICON_ERROR)
            return
        dlg = wx.TextEntryDialog(self, u'Введите название нового альбома',
                                 u'Создаем новый альбом в "{}"'.format(album.title))
        if dlg.ShowModal() == wx.ID_OK:
            new_album = album.create_album(dlg.GetValue())
            if not new_album:
                return
            new_item_id = self.AppendAlbum(item_id, new_album)
            self.albumTree.Expand(item_id)
            self.albumTree.SelectItem(new_item_id)
            self.LoadAlbum(new_item_id, new_album)
        dlg.Destroy()

    def DeleteSelectedAlbum(self):
        item_id, album = self.GetSelectedAlbum()
        if wx.MessageBox(u'Вы действительно хотите удалить альбом "{}"?'.format(album.title),
                         u'Внимание', wx.YES_NO | wx.ICON_WARNING) == wx.YES:
            album.delete()
            self.albumTree.Delete(item_id)

    def UploadSelectedPhoto(self):
        f = lambda p: self.current_album.upload(p.data)
        self.ForEachSelected(f, u'Загрузить фотографии?')
